#include <iostream>
#include <cstring>
#include <unistd.h>
#include <functional>
#include <unordered_map>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <pthread.h>

#include "LogMessage.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
#include "Pthread.hpp"

using task_t = std::function<void()>;
using callback_t = std::function<void(std::string&)>;

class TcpServer;

struct ThreadData
{
    ThreadData(int sockfd, InetAddr user, TcpServer *self)
        : _sockfd(sockfd), _user(user), _self(self)
    {
    }

public:
    int _sockfd;
    InetAddr _user;
    TcpServer *_self;
};

class TcpServer
{
    const int backlog = 20;

public:
    TcpServer(uint16_t &port, std::string ip = "")
    {
        // 创建套接字
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            lg.LogMessage(Error, "create socket error");
            exit(1);
        }
        lg.LogMessage(Normal, "create socket success, socket:%d", _listensock);

        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        // 绑定IP和端口
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = ip.empty() ? INADDR_ANY : inet_addr(ip.c_str());
        if (bind(_listensock, (sockaddr *)&addr, sizeof(addr)) < 0)
        {
            lg.LogMessage(Error, "bind error");
            exit(2);
        }
        lg.LogMessage(Normal, "bind success");

        // 监听listen
        if (listen(_listensock, backlog) < 0)
        {
            lg.LogMessage(Error, "listen error");
            exit(3);
        }
        lg.LogMessage(Normal, "listen success");

        ThreadPool<task_t>::GetInstance()->Start();
    }

    ~TcpServer()
    {
        close(_listensock);
    }

    void Server(int serverSocket, InetAddr user)
    {
        sockaddr_in addr = user.GetAddr();
        socklen_t addrlen = sizeof(addr);
        char temp[1024];
        while (true)
        {
            std::string buffer = "[" + user.GetUser() + "] ";
            int n = recvfrom(serverSocket, temp, sizeof(temp) - 1, 0, (sockaddr *)&addr, &addrlen);
            if (n > 0)
            {
                temp[n] = 0;
                buffer += temp;
                std::cout << buffer << std::endl;
            }
            else if (n == 0)
            {
                std::cout << "you close the connect, me too" << std::endl;
                break;
            }
            else
            {
                std::cout << "recvfrom error" << std::endl;
                break;
            }
        }
    }

    static void *Handler(void *arg)
    {
        pthread_detach(pthread_self());
        ThreadData *data = static_cast<ThreadData *>(arg);
        data->_self->Server(data->_sockfd, data->_user);
        close(data->_sockfd);
    }

    void Start()
    {
        signal(SIGCHLD, SIG_IGN); // 子进程退出手动释放资源
        while (true)
        {
            struct sockaddr_in addr;
            socklen_t addrlen = sizeof(addr);
            memset(&addr, 0, sizeof(addr));
            int serverSocket = accept(_listensock, (sockaddr *)&addr, &addrlen);
            // 从这往后，都使用serverSocket
            InetAddr user(addr);

            // V1单进程
            // Server(serverSocket, user);
            // close(serverSocket);

            // V2多进程
            // int fd = fork();
            // if (fd == 0)
            // {
            //     //child
            //     close(_listensock);
            //     if (fork() > 0)
            //     {
            //         exit(0);
            //     }
            //     Server(serverSocket, user);
            //     //孙子进程
            //     close(serverSocket);
            //     exit(0);
            // }
            // else if (fd > 0)
            // {
            //     //father
            //     close(serverSocket);
            //     waitpid(fd, nullptr, 0);
            // }
            // else
            // {
            //     lg.LogMessage(Warning, "fork error");
            // }

            // V3多进程信号版

            // int fd = fork();
            // if (fd == 0)
            // {
            //     // child
            //     close(_listensock);
            //     Server(serverSocket, user);
            //     close(serverSocket);
            //     exit(0);
            // }
            // else if (fd > 0)
            // {
            //     // father
            //     close(serverSocket);
            // }
            // else
            // {
            //     lg.LogMessage(Warning, "fork error");
            // }

            // V4多线程版
            // pthread_t p;
            // ThreadData *data = new ThreadData(serverSocket, user, this);
            // pthread_create(&p, nullptr, Handler, (void *)data);

            // V5线程池版
            task_t task = std::bind(&TcpServer::Routine, this, serverSocket, user);
            ThreadPool<task_t>::GetInstance()->Push(task);
        }

        // close(serverSocket)
    }

    void Routine(int sockfd, InetAddr user)
    {
        // 向用户发送有哪些功能
        std::string str = GetAllFunction();
        Sendto(sockfd, user, str);

        // 读取用户的选择
        std::string buffer;
        Recvfrom(sockfd, buffer);

        // 读取用户的输入
        std::string message;
        Recvfrom(sockfd, message);

        // 根据选择执行相应的方法
        auto func = _funcs.find(buffer);
        if (func != _funcs.end())
        {
            func->second(message);
        }
        else 
        {
            message = "choise the tast not exist";
        }
        //将数据写回
        Sendto(sockfd, user, message);
        std::cout << message << std::endl;

        close(sockfd);
    }

    void Sendto(int sockfd, InetAddr user, std::string buffer)
    {
        sockaddr_in addr = user.GetAddr();
        socklen_t addrlen = sizeof(addr);

        sendto(sockfd, buffer.c_str(), buffer.size(), 0, (sockaddr *)&addr, addrlen);
    }

    void Recvfrom(int sockfd, std::string& buffer)
    {
        char temp[1024];
        sockaddr_in addr;
        socklen_t addrlen = sizeof(addr);
        int n = recvfrom(sockfd, temp, sizeof(temp) - 1, 0, (sockaddr *)&addr, &addrlen);
        if (n > 0)
        {
            temp[n] = 0;
            buffer = temp;
        }
        else if (n == 0)
        {
            std::cout << "you close the connect, me too" << std::endl;
        }
        else
        {
            std::cout << "recvfrom error" << std::endl;
        }
    }

    std::string GetAllFunction()
    {
        std::string buffer = "| "; 
        for (auto fun : _funcs)
        {
            buffer += fun.first;
            buffer += " | ";
        }

        return buffer;
    }

    void AddFunc(std::string func_name, callback_t task)
    {
        _funcs.insert({func_name, task});
    }

private:
    int _listensock;
    std::unordered_map<std::string, callback_t> _funcs;
    Log lg;
};